package com.vegetable.common.aspect;

import com.alibaba.fastjson.JSONObject;
import com.google.gson.Gson;
import com.vegetable.common.annotation.SysLog;
import com.vegetable.common.utils.EntityDiffUtils;
import com.vegetable.common.utils.HttpContextUtils;
import com.vegetable.common.utils.IPUtils;
import com.vegetable.common.utils.SpringContextUtils;
import com.vegetable.modules.entity.sys.SysLogEntity;
import com.vegetable.modules.entity.sys.SysUserEntity;
import com.vegetable.modules.service.sys.SysLogService;
import lombok.extern.slf4j.Slf4j;
import org.apache.shiro.SecurityUtils;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.Method;
import java.util.Date;
import java.util.List;
import java.util.Map;


/**
 * 系统日志，切面处理类
 *
 * @author Mark sunlightcs@gmail.com
 */
@Slf4j
@Aspect
@Component
public class SysLogAspect {
	@Autowired
	private SysLogService sysLogService;
	
	@Pointcut("execution(* com.vegetable.modules.controller..*.*(..))")
	public void logPointCut() { 
		
	}

	@Around("logPointCut()")
	public Object around(ProceedingJoinPoint point) throws Throwable {
		long beginTime = System.currentTimeMillis();
		//执行方法
		Object result = point.proceed();
		//执行时长(毫秒)
		long time = System.currentTimeMillis() - beginTime;
		//保存日志
		saveSysLog(point, time, result);
		return result;
	}

	private void saveSysLog(ProceedingJoinPoint joinPoint, long time, Object result) throws Exception{
		MethodSignature signature = (MethodSignature) joinPoint.getSignature();
		Method method = signature.getMethod();

		SysLogEntity sysLog = new SysLogEntity();
		SysLog syslog = method.getAnnotation(SysLog.class);
		if(syslog != null){
			//注解上的描述
			sysLog.setOperation(syslog.value());
			
			// 如果需要记录实体变更
			if (syslog.recordEntityChange()) {
				handleEntityChangeLog(joinPoint, result, sysLog, syslog);
			}
		}

		//请求的方法名
		String className = joinPoint.getTarget().getClass().getName();
		String methodName = signature.getName();
		sysLog.setMethod(className + "." + methodName + "()");

		//请求的参数
		Object[] args = joinPoint.getArgs();
		try{
			String params = new Gson().toJson(args);
			sysLog.setParams(params);
			sysLog.setResult(JSONObject.toJSONString(result));
		}catch (Exception e){
			e.printStackTrace();
		}
		//获取request
		HttpServletRequest request = HttpContextUtils.getHttpServletRequest();
		//设置IP地址
		sysLog.setIp(IPUtils.getIpAddr(request));
		String userAgent = request.getHeader("User-Agent");
		sysLog.setUserAgent(userAgent);
		try {
			//用户名
			String username = ((SysUserEntity) SecurityUtils.getSubject().getPrincipal()).getUsername();
			sysLog.setUsername(username);
			sysLog.setTime(time);
			sysLog.setCreateDate(new Date());
			//保存系统日志
			sysLogService.save(sysLog);
		}catch (Exception e){
			e.printStackTrace();
		}
	}
	
	/**
	 * 处理实体变更日志
	 * @param joinPoint 切点
	 * @param result 方法执行结果
	 * @param sysLog 日志实体
	 * @param syslog 注解
	 */
	private void handleEntityChangeLog(ProceedingJoinPoint joinPoint, Object result, SysLogEntity sysLog, SysLog syslog) {
		try {
			log.debug("开始处理实体变更日志");
			
			// 设置实体类信息
			Class<?> entityClass = syslog.entityClass();
			log.debug("实体类信息: {}", entityClass);
			
			if (entityClass != null && !entityClass.equals(Object.class)) {
				sysLog.setEntityClass(entityClass.getName());
				log.debug("设置实体类名称: {}", entityClass.getName());
			}
			
			// 根据方法名判断操作类型
			String methodName = joinPoint.getSignature().getName();
			log.debug("方法名: {}", methodName);
			
			String operationType = "UNKNOWN";
			if (methodName.startsWith("save") || methodName.startsWith("insert") || methodName.startsWith("add")) {
				operationType = "INSERT";
			} else if (methodName.startsWith("update") || methodName.startsWith("modify") || methodName.startsWith("submit") || methodName.startsWith("current")) {
				operationType = "UPDATE";
			} else if (methodName.startsWith("delete") || methodName.startsWith("remove")) {
				operationType = "DELETE";
			} else if (methodName.startsWith("get") || methodName.startsWith("find") || methodName.startsWith("query")) {
				operationType = "SELECT";
			}
			
			log.debug("操作类型: {}", operationType);
			sysLog.setOperationType(operationType);
			
			// 对于更新操作，尝试获取修改前后的数据
			if ("UPDATE".equals(operationType)) {
				log.debug("处理UPDATE操作");
				
				// 获取方法参数
				Object[] args = joinPoint.getArgs();
				log.debug("方法参数数量: {}", args.length);
				
				if (args.length > 0) {
					for (int i = 0; i < args.length; i++) {
						Object arg = args[i];
						log.debug("参数[{}]: {}", i, arg);
						
						if (arg != null) {
							// 如果参数是实体对象，记录修改前后的数据
							if (entityClass != null && !entityClass.equals(Object.class) && 
							    entityClass.isAssignableFrom(arg.getClass())) {
								log.debug("参数是目标实体类的实例，开始记录数据");
								
								// 记录修改后的数据
								String afterData = EntityDiffUtils.toJson(arg);
								sysLog.setAfterData(afterData);
								log.debug("记录修改后的数据: {}", afterData);
								
								// 尝试获取修改前的数据
								try {
									Object idValue = getIdValue(arg);
									log.debug("实体ID值: {}", idValue);
									
									if (idValue != null) {
										// 通过反射获取对应的服务类并查询旧数据
										String serviceName = getEntityServiceName(entityClass);
										log.debug("尝试获取服务Bean: {}", serviceName);
										
										if (SpringContextUtils.containsBean(serviceName)) {
											Object service = SpringContextUtils.getBean(serviceName);
											if (service != null) {
												log.debug("成功获取服务Bean实例");
												Method selectByIdMethod = service.getClass().getMethod("getById", Long.class);
												Object oldEntity = selectByIdMethod.invoke(service, Long.valueOf(idValue.toString()));
												if (oldEntity != null) {
													String beforeData = EntityDiffUtils.toJson(oldEntity);
													sysLog.setBeforeData(beforeData);
													log.debug("记录修改前的数据: {}", beforeData);
												} else {
													log.warn("未找到ID为{}的实体记录", idValue);
												}
											} else {
												log.warn("无法获取服务Bean: {}", serviceName);
											}
										} else {
											// 尝试使用其他可能的Bean名称
											String alternativeName = entityClass.getSimpleName() + "Service";
											log.debug("尝试获取替代服务Bean: {}", alternativeName);
											
											if (SpringContextUtils.containsBean(alternativeName)) {
												Object service = SpringContextUtils.getBean(alternativeName);
												if (service != null) {
													Method selectByIdMethod = service.getClass().getMethod("getById", Long.class);
													Object oldEntity = selectByIdMethod.invoke(service, Long.valueOf(idValue.toString()));
													if (oldEntity != null) {
														String beforeData = EntityDiffUtils.toJson(oldEntity);
														sysLog.setBeforeData(beforeData);
														log.debug("记录修改前的数据: {}", beforeData);
													}
												}
											} else {
												log.warn("未找到服务Bean: {} 或 {}", serviceName, alternativeName);
											}
										}
									}
								} catch (Exception e) {
									log.warn("获取实体修改前数据失败: {}", e.getMessage(), e);
								}
							}
							
							// 如果参数包含ID，记录ID
							if (hasIdProperty(arg)) {
								String idValue = getIdValue(arg);
								sysLog.setEntityId(idValue);
								log.debug("记录实体ID: {}", idValue);
							}
						}
					}
				}
			} else if ("INSERT".equals(operationType)) {
				log.debug("处理INSERT操作");
				
				// 插入操作记录新数据
				Object[] args = joinPoint.getArgs();
				log.debug("方法参数数量: {}", args.length);
				
				for (int i = 0; i < args.length; i++) {
					Object arg = args[i];
					log.debug("参数[{}]: {}", i, arg);
					
					if (arg != null && entityClass != null && !entityClass.equals(Object.class) && 
					    entityClass.isAssignableFrom(arg.getClass())) {
						String afterData = EntityDiffUtils.toJson(arg);
						sysLog.setAfterData(afterData);
						log.debug("记录新插入的数据: {}", afterData);
						break;
					}
				}
			} else if ("DELETE".equals(operationType)) {
				log.debug("处理DELETE操作");
				
				// 删除操作记录ID和删除前的数据
				Object[] args = joinPoint.getArgs();
				log.debug("方法参数数量: {}", args.length);
				
				for (int i = 0; i < args.length; i++) {
					Object arg = args[i];
					log.debug("参数[{}]: {}", i, arg);
					
					if (arg != null) {
						if (hasIdProperty(arg)) {
							String idValue = getIdValue(arg);
							sysLog.setEntityId(idValue);
							log.debug("记录删除的实体ID: {}", idValue);
							
							// 尝试获取删除前的数据
							try {
								if (idValue != null && entityClass != null && !entityClass.equals(Object.class)) {
									// 通过反射获取对应的服务类并查询旧数据
									String serviceName = getEntityServiceName(entityClass);
									log.debug("尝试获取服务Bean: {}", serviceName);
									
									if (SpringContextUtils.containsBean(serviceName)) {
										Object service = SpringContextUtils.getBean(serviceName);
										if (service != null) {
											Method selectByIdMethod = service.getClass().getMethod("getById", Long.class);
											Object oldEntity = selectByIdMethod.invoke(service, Long.valueOf(idValue));
											if (oldEntity != null) {
												String beforeData = EntityDiffUtils.toJson(oldEntity);
												sysLog.setBeforeData(beforeData);
												log.debug("记录删除前的数据: {}", beforeData);
											}
										}
									} else {
										// 尝试使用其他可能的Bean名称
										String alternativeName = entityClass.getSimpleName() + "Service";
										log.debug("尝试获取替代服务Bean: {}", alternativeName);
										
										if (SpringContextUtils.containsBean(alternativeName)) {
											Object service = SpringContextUtils.getBean(alternativeName);
											if (service != null) {
												Method selectByIdMethod = service.getClass().getMethod("getById", Long.class);
												Object oldEntity = selectByIdMethod.invoke(service, Long.valueOf(idValue));
												if (oldEntity != null) {
													String beforeData = EntityDiffUtils.toJson(oldEntity);
													sysLog.setBeforeData(beforeData);
													log.debug("记录删除前的数据: {}", beforeData);
												}
											}
										} else {
											log.warn("未找到服务Bean: {} 或 {}", serviceName, alternativeName);
										}
									}
								}
							} catch (Exception e) {
								log.warn("获取实体删除前数据失败: {}", e.getMessage(), e);
							}
						}
						break;
					}
				}
			}
			
			// 添加调试日志，检查是否正确捕获了数据
			log.debug("实体变更处理完成 - BeforeData: {}, AfterData: {}", sysLog.getBeforeData(), sysLog.getAfterData());
			
			// 生成字段变更详情（使用新的方法）
			if (sysLog.getBeforeData() != null || sysLog.getAfterData() != null) {
				log.debug("开始生成详细字段变更信息");
				log.debug("BeforeData: {}", sysLog.getBeforeData());
				log.debug("AfterData: {}", sysLog.getAfterData());
				
				// 使用新的方法生成包含字段描述的变更详情
				generateDetailedFieldChanges(sysLog, entityClass);
				log.debug("详细字段变更信息生成完成");
			} else {
				log.debug("没有BeforeData或AfterData，跳过详细字段变更信息生成");
			}
			
			// 记录调试信息
			log.debug("实体变更日志记录 - 操作类型: {}, 实体类: {}, 实体ID: {}, BeforeData: {}, AfterData: {}, DetailedFieldChanges: {}", 
			         operationType, sysLog.getEntityClass(), sysLog.getEntityId(), 
			         sysLog.getBeforeData(), sysLog.getAfterData(), sysLog.getDetailedFieldChanges());
		} catch (Exception e) {
			log.error("处理实体变更日志时出错", e);
		}
	}
	
	/**
	 * 生成详细的字段变更信息（包含字段描述）
	 * @param sysLog 日志实体
	 * @param entityClass 实体类
	 */
	private void generateDetailedFieldChanges(SysLogEntity sysLog, Class<?> entityClass) {
		try {
			log.debug("generateDetailedFieldChanges开始执行，entityClass: {}", entityClass);
			// 如果有实体类信息，尝试创建实体对象进行比较
			if (entityClass != null && !entityClass.equals(Object.class)) {
				// 反序列化beforeData和afterData为实体对象
				Object beforeObj = null;
				Object afterObj = null;
				
				if (sysLog.getBeforeData() != null && !sysLog.getBeforeData().isEmpty()) {
					log.debug("反序列化beforeData: {}", sysLog.getBeforeData());
					beforeObj = EntityDiffUtils.getObjectMapper().readValue(sysLog.getBeforeData(), entityClass);
					log.debug("反序列化beforeData成功，对象类型: {}", beforeObj.getClass().getName());
				}
				
				if (sysLog.getAfterData() != null && !sysLog.getAfterData().isEmpty()) {
					log.debug("反序列化afterData: {}", sysLog.getAfterData());
					afterObj = EntityDiffUtils.getObjectMapper().readValue(sysLog.getAfterData(), entityClass);
					log.debug("反序列化afterData成功，对象类型: {}", afterObj.getClass().getName());
				}
				
				log.debug("开始调用compareObjectsWithDescriptions");
				// 使用EntityDiffUtils.compareObjectsWithDescriptions生成详细的字段变更信息
				List<Map<String, Object>> detailedChanges = EntityDiffUtils.compareObjectsWithDescriptions(beforeObj, afterObj);
				log.debug("compareObjectsWithDescriptions返回结果数量: {}", detailedChanges.size());
				
				// 打印详细变更信息
				for (Map<String, Object> change : detailedChanges) {
					log.debug("详细变更 - 字段: {}, 描述: {}, 旧值: {}, 新值: {}", 
                             change.get("fieldName"), change.get("fieldDescription"), 
                             change.get("oldValue"), change.get("newValue"));
				}
				
				sysLog.setDetailedFieldChanges(detailedChanges);
				log.debug("设置detailedFieldChanges完成");
			} else {
				log.debug("entityClass为空或为Object.class，跳过详细字段变更信息生成");
			}
		} catch (Exception e) {
			log.error("生成详细字段变更信息时出错", e);
		}
	}
	
	/**
	 * 根据实体类获取对应的服务类名称
	 */
	private String getEntityServiceName(Class<?> entityClass) {
		String entityName = entityClass.getSimpleName();
		// 将首字母转换为小写
		String firstLetter = entityName.substring(0, 1).toLowerCase();
		String serviceName = firstLetter + entityName.substring(1) + "ServiceImpl";
		
		// 添加调试日志
		log.debug("实体类: {}, 推测的服务类名称: {}", entityClass.getName(), serviceName);
		
		// 检查是否存在该服务Bean
		if (SpringContextUtils.containsBean(serviceName)) {
			log.debug("找到匹配的服务Bean: {}", serviceName);
			return serviceName;
		}
		
		// 尝试其他可能的命名方式
		String alternativeName1 = entityName + "ServiceImpl";
		String alternativeName2 = entityName + "Service";
		String alternativeName3 = firstLetter + entityName.substring(1) + "Service";
		
		log.debug("尝试其他命名方式: {}, {}, {}", alternativeName1, alternativeName2, alternativeName3);
		
		if (SpringContextUtils.containsBean(alternativeName1)) {
			log.debug("找到匹配的服务Bean: {}", alternativeName1);
			return alternativeName1;
		}
		
		if (SpringContextUtils.containsBean(alternativeName2)) {
			log.debug("找到匹配的服务Bean: {}", alternativeName2);
			return alternativeName2;
		}
		
		if (SpringContextUtils.containsBean(alternativeName3)) {
			log.debug("找到匹配的服务Bean: {}", alternativeName3);
			return alternativeName3;
		}
		
		log.warn("未找到匹配的服务Bean，返回默认名称: {}", serviceName);
		return serviceName;
	}
	
	/**
	 * 检查对象是否有ID属性
	 */
	private boolean hasIdProperty(Object obj) {
		try {
			return obj.getClass().getDeclaredField("id") != null;
		} catch (NoSuchFieldException e) {
			return false;
		}
	}
	
	/**
	 * 获取对象的ID值
	 */
	private String getIdValue(Object obj) {
		try {
			java.lang.reflect.Field idField = obj.getClass().getDeclaredField("id");
			idField.setAccessible(true);
			Object idValue = idField.get(obj);
			return idValue != null ? idValue.toString() : null;
		} catch (Exception e) {
			return null;
		}
	}
}